<!DOCTYPE html>
<html lang="cn" dir="auto">

<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>Spring 泛型 | 染竹君的个人博客</title>
<meta name="keywords" content="Java, Spring, Generic">
<meta name="description" content="Java 中的 Type 家族，Spring 针对 Tyep 的封装——ResolvableType 及其应用示例。">
<meta name="author" content="SadBird">
<link rel="canonical" href="https://www.liyangjie.cn/posts/work/spring-generic/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.4b7d810bc0c98044b1c7a72962d55d7f125a07c3ed5cf31e670c3fdecc98b341.css" integrity="sha256-S32BC8DJgESxx6cpYtVdfxJaB8PtXPMeZww/3syYs0E=" rel="preload stylesheet" as="style">
<link rel="icon" href="https://www.liyangjie.cn/favicon/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://www.liyangjie.cn/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://www.liyangjie.cn/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" href="https://www.liyangjie.cn/favicon/apple-touch-icon.png">
<link rel="mask-icon" href="https://www.liyangjie.cn/favicon/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
    <style>
        #theme-toggle,
        .top-link {
            display: none;
        }

    </style>
    <style>
        @media (prefers-color-scheme: dark) {
            :root {
                --theme: rgb(29, 30, 32);
                --entry: rgb(46, 46, 51);
                --primary: rgb(218, 218, 219);
                --secondary: rgb(155, 156, 157);
                --tertiary: rgb(65, 66, 68);
                --content: rgb(196, 196, 197);
                --hljs-bg: rgb(46, 46, 51);
                --code-bg: rgb(55, 56, 62);
                --border: rgb(51, 51, 51);
            }

            .list {
                background: var(--theme);
            }

            .list:not(.dark)::-webkit-scrollbar-track {
                background: 0 0;
            }

            .list:not(.dark)::-webkit-scrollbar-thumb {
                border-color: var(--theme);
            }
        }

    </style>
</noscript>
<meta name="baidu-site-verification" content="code-9oLyeix0aK" />
<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?4a41bf85d719f0e8c3165fc76904f546";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script async src="https://www.googletagmanager.com/gtag/js?id=G-C6GDZ56F4S"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
	window.dataLayer = window.dataLayer || [];
	function gtag(){dataLayer.push(arguments);}
	gtag('js', new Date());
	gtag('config', 'G-C6GDZ56F4S', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="Spring 泛型" />
<meta property="og:description" content="Java 中的 Type 家族，Spring 针对 Tyep 的封装——ResolvableType 及其应用示例。" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://www.liyangjie.cn/posts/work/spring-generic/" />
<meta property="og:image" content="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" /><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2021-09-13T21:56:33&#43;08:00" />
<meta property="article:modified_time" content="2021-09-13T21:56:33&#43;08:00" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" />
<meta name="twitter:title" content="Spring 泛型"/>
<meta name="twitter:description" content="Java 中的 Type 家族，Spring 针对 Tyep 的封装——ResolvableType 及其应用示例。"/>


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position":  1 ,
      "name": "Posts",
      "item": "https://www.liyangjie.cn/posts/"
    }, 
    {
      "@type": "ListItem",
      "position":  2 ,
      "name": "Work",
      "item": "https://www.liyangjie.cn/posts/work/"
    }, 
    {
      "@type": "ListItem",
      "position":  3 ,
      "name": "Spring 泛型",
      "item": "https://www.liyangjie.cn/posts/work/spring-generic/"
    }
  ]
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Spring 泛型",
  "name": "Spring 泛型",
  "description": "Java 中的 Type 家族，Spring 针对 Tyep 的封装——ResolvableType 及其应用示例。",
  "keywords": [
    "Java", "Spring", "Generic"
  ],
  "articleBody": "Type 概述 1 2 3 4 5  public interface Type { default String getTypeName() { return toString(); } }   Java Type\r\rType is the common super interface for all types in the Java programming language. These include raw types, parameterized types, array types, type variables and primitive types.\r\r JDK 中的 Type 是一种高级抽象，其类图关系如下，它代表了 Java 中所有与 类型 相关的概念，此处的 类型 与我们常用的 基础数据类型 和 引用数据类型 的概念不同，它的抽象层级更高，概念更加宽泛，主要用于支持泛型中的类型处理。\n\r\r除了我们熟悉的 Class 之外，还有如下子接口：\n ParameterizedType TypeVariable GenericArrayType WildcardType  下面小节将分别使用示例代码进行介绍。\n ParameterizedType 1 2 3 4 5 6 7 8  public interface ParameterizedType extends Type { Type[] getActualTypeArguments(); Type getRawType(); Type getOwnerType(); }   ParameterizedType 形如 List ，即原生类型加上泛型参数的形式，其中，泛型参数可以有多个，并且既可以是实际具体类型（如 String），也可以是参数形式（如 T,K,E），还可以是通配符形式（如 ? extends Number）。各方法返回结果示例如下：\n\r\r以下代码使用反射的方式进行实验，其中 getGenericType() 方法可以获取字段的泛型，对 getActualTypeArguments() 方法获取到的类型数组进行进一步的分析，可以发现它支持所有的 Type 子类型，代码有些部分暂时看不懂可以先放放，等后续章节看完再回头来看：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58  public class ParameterizedTypeDemoK extends Number { Map.EntryK, String entry; ListString stringList; MapK[], ? extends Number mapWithWildcard; MapString, ListString mapWithList; OuterClassK.InnerClassK ownerTest; public static void main(String[] args) { Class clazz = ParameterizedTypeDemo.class; Field[] declaredFields = clazz.getDeclaredFields(); printHorizonLine(); Arrays.stream(declaredFields).forEach(ParameterizedTypeDemo::showFieldType); } private static void printHorizonLine() { System.out.println(\"---------------------------------\"); } private static void showFieldType(Field field) { Type type = field.getGenericType(); if (type instanceof ParameterizedType) { printFieldTypeInfo(field, type); printHorizonLine(); } } private static void printFieldTypeInfo(Field field, Type type) { ParameterizedType parameterizedType = (ParameterizedType) type; Type rawType = parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type ownerType = parameterizedType.getOwnerType(); System.out.printf(\"Field '%s' ParameterizedType %s attrs: \\n\", field.getName(), type.getTypeName()); System.out.printf(\"[RawType]: %s, [OwnerType]: %s \\n\", rawType, ownerType); System.out.println(\"[ActualTypeArguments]:\"); Arrays.stream(actualTypeArguments).forEach(ParameterizedTypeDemo::printActualArguments); } private static void printActualArguments(Type t) { if (t instanceof ParameterizedType) { System.out.printf(\"(P) %s \\n\", t); } else if (t instanceof TypeVariable) { System.out.printf(\"(T) %s \\n\", t); } else if (t instanceof WildcardType) { System.out.printf(\"(W) %s \\n\", t); } else if (t instanceof GenericArrayType) { System.out.printf(\"(A) %s \\n\", t); } else if (t instanceof Class) { System.out.printf(\"(C) %s \\n\", t); } } } class OuterClassT { class InnerClassS { } }   输出结果为：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  --------------------------------- Field 'entry' ParameterizedType java.util.Map$EntryK, java.lang.String attrs: [RawType]: interface java.util.Map$Entry, [OwnerType]: interface java.util.Map [ActualTypeArguments]: (T) K (C) class java.lang.String --------------------------------- Field 'stringList' ParameterizedType java.util.Listjava.lang.String attrs: [RawType]: interface java.util.List, [OwnerType]: null [ActualTypeArguments]: (C) class java.lang.String --------------------------------- Field 'mapWithWildcard' ParameterizedType java.util.MapK[], ? extends java.lang.Number attrs: [RawType]: interface java.util.Map, [OwnerType]: null [ActualTypeArguments]: (A) K[] (W) ? extends java.lang.Number --------------------------------- Field 'mapWithList' ParameterizedType java.util.Mapjava.lang.String, java.util.Listjava.lang.String attrs: [RawType]: interface java.util.Map, [OwnerType]: null [ActualTypeArguments]: (C) class java.lang.String (P) java.util.Listjava.lang.String --------------------------------- Field 'ownerTest' ParameterizedType cn.liyangjie.spring.OuterClassK$InnerClassK attrs: [RawType]: class cn.liyangjie.spring.OuterClass$InnerClass, [OwnerType]: cn.liyangjie.spring.OuterClassK [ActualTypeArguments]: (T) K ---------------------------------    TypeVariable 1 2 3 4 5 6 7 8 9  public interface TypeVariableD extends GenericDeclaration extends Type, AnnotatedElement { Type[] getBounds(); D getGenericDeclaration(); String getName(); AnnotatedType[] getAnnotatedBounds(); }   TypeVariable 表示的是 类型参数 的概念，即泛型定义中使用到的 T, K, E 等，如 JDK 中提供的 List 类，其含有一个 TypeVariable 为 E。\n1  public interface ListE extends CollectionE...   再次观察 TypeVariable 接口可以发现其自身含有一个上界为 GenericDeclaration 的类型参数，其类图如下：\n\r\r字面意义上，它表示的是 泛型声明，Java 规范中，泛型的定义可以出现在 类 和 方法 上（构造器也可归属于方法），正好对应了该上图中的 3 个实现类。因此 getGenericDeclaration() 方法是为了获取该参数所定义的「位置」。以下代码分别定义了 4 个 TypeVariable ，分别展示了 3 种不同位置及 Java8 后引入的 TypeAnnotation。\n1 2 3 4  @Target({ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface TypeBoundAnnotationT { }   1 2 3 4  @Target({ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface TypeBoundAnnotationE { }   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51  public class TypeVariableDemoT extends @TypeBoundAnnotationT ComparableT, E extends @TypeBoundAnnotationT @TypeBoundAnnotationE Number \u0026 @TypeBoundAnnotationE CharSequence { public static void main(String[] args) { Class typeVariableClazz = TypeVariableDemo.class; TypeVariable extends GenericDeclaration[] typeParameters = typeVariableClazz.getTypeParameters(); printHorizonLine(); Arrays.stream(typeParameters).forEach(TypeVariableDemo::showTypeVariable); Class methodAndConstructorTestClazz = MethodAndConstructorTest.class; Constructor[] declaredConstructors = methodAndConstructorTestClazz.getDeclaredConstructors(); Method[] declaredMethods = methodAndConstructorTestClazz.getDeclaredMethods(); showMethodTypeVariables(declaredConstructors); showMethodTypeVariables(declaredMethods); } private static void printHorizonLine() { System.out.println(\"---------------------------------\"); } private static void showTypeVariable(TypeVariable extends GenericDeclaration tv) { String name = tv.getName(); Type[] bounds = tv.getBounds(); AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds(); GenericDeclaration genericDeclaration = tv.getGenericDeclaration(); System.out.printf(\"[TypeVariableName]: %s\\n\", name); System.out.printf(\"[Bounds]: %s\\n\", Arrays.toString(bounds)); System.out.printf(\"[GenericDeclaration]: %s\\n\", genericDeclaration); System.out.println(\"[AnnotatedBounds]: \"); Arrays.stream(annotatedBounds).forEach(a - { System.out.printf(\"%s\\n\", Arrays.toString(a.getAnnotations())); }); printHorizonLine(); } private static void showMethodTypeVariables(GenericDeclaration[] declaredConstructors) { Arrays.stream(declaredConstructors).forEach(c - { TypeVariable[] constructorTypeParameters = c.getTypeParameters(); Arrays.stream(constructorTypeParameters).forEach(TypeVariableDemo::showTypeVariable); }); } } class MethodAndConstructorTest { K MethodAndConstructorTest(K k){} public S void methodTest(S s){} }   输出结果如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26  --------------------------------- [TypeVariableName]: T [Bounds]: [java.lang.ComparableT] [GenericDeclaration]: class cn.liyangjie.spring.TypeVariableDemo [AnnotatedBounds]: [@cn.liyangjie.spring.TypeBoundAnnotationT()] --------------------------------- [TypeVariableName]: E [Bounds]: [class java.lang.Number, interface java.lang.CharSequence] [GenericDeclaration]: class cn.liyangjie.spring.TypeVariableDemo [AnnotatedBounds]: [@cn.liyangjie.spring.TypeBoundAnnotationT(), @cn.liyangjie.spring.TypeBoundAnnotationE()] [@cn.liyangjie.spring.TypeBoundAnnotationE()] --------------------------------- [TypeVariableName]: K [Bounds]: [class java.lang.Object] [GenericDeclaration]: cn.liyangjie.spring.MethodAndConstructorTest(java.lang.Object) [AnnotatedBounds]: [] --------------------------------- [TypeVariableName]: S [Bounds]: [class java.lang.Object] [GenericDeclaration]: public void cn.liyangjie.spring.MethodAndConstructorTest.methodTest(java.lang.Object) [AnnotatedBounds]: [] ---------------------------------    GenericArrayType 1 2 3  public interface GenericArrayType extends Type { Type getGenericComponentType(); }   GenericArrayType 表示形如 T[], T[][] 等 泛型数组，它有一个方法，获取数组中所存储元素的 Type。示例代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24  public class GenericArrayTypeDemoT { T[] tA; T[][] tAA; ListT[] listA; List extends T[] wildcardListA; String[] stringA; public static void main(String[] args) { Class clazz = GenericArrayTypeDemo.class; Field[] fields = clazz.getDeclaredFields(); Arrays.stream(fields).forEach(GenericArrayTypeDemo::showGenericTypeComponentType); } private static void showGenericTypeComponentType(Field f) { String fieldName = f.getName(); Type fieldType = f.getGenericType(); if (fieldType instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) fieldType; System.out.printf(\"[Field: %s] Component Type is: %s\\n\", fieldName, genericArrayType.getGenericComponentType()); } else { System.out.printf(\"[Field: %s] is not GenericArrayType\\n\", fieldName); } } }   输出结果如下：\n1 2 3 4 5  [Field: tA] Component Type is: T [Field: tAA] Component Type is: T[] [Field: listA] Component Type is: java.util.ListT [Field: wildcardListA] Component Type is: java.util.List extends T [Field: stringA] is not GenericArrayType   GenericType 相对来说还是好理解的，数组中的元素类型即为数组拿掉一个 [] 符号后对应的类型。\n为了提升理解，这里留下一个问题：上述输出结果中， T, T[], List, List 分别是什么类型？\n答案\r\r1 2 3 4  [Field: tA's component type] is TypeVariable [Field: tAA's component type] is GenericArrayType [Field: listA's component type] is ParameterizedType [Field: wildcardListA's component type] is ParameterizedType   \r\r  WildcardType 1 2 3 4 5 6  public interface WildcardType extends Type { Type[] getUpperBounds(); Type[] getLowerBounds(); }   顾名思义，它表示的是 ? extends Number 形式的 通配符类型，接口中的两个方法也比较好理解，分别代表通配符的上界和下界。示例代码如下，为了简单起见，仅仅定义了一个 Field，且使用的时候直接取数组下标 0：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  public class WildcardTypeDemo { List extends Number wildcardList; public static void main(String[] args) { Class clazz = WildcardTypeDemo.class; Field wildcardField = clazz.getDeclaredFields()[0]; Type genericType = wildcardField.getGenericType(); //List if (genericType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericType; Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; // ? extends Number  if (actualTypeArgument instanceof WildcardType) { WildcardType wildcardType = (WildcardType) actualTypeArgument; System.out.printf(\"[Field %s is WildcardType]: %s, its upper bound is %s and lower bound is %s\", wildcardField.getName(), wildcardType, Arrays.toString(wildcardType.getUpperBounds()), Arrays.toString(wildcardType.getLowerBounds())); } } } }   输出结果为：\n1  [Field wildcardList is WildcardType]: ? extends java.lang.Number, its upper bound is [class java.lang.Number] and lower bound is []    Spring 中的 ResolvableType 惯例先贴出 Spring 官方对于该类的简介：\nResolvableType\r\rEncapsulates a Java Type, providing access to supertypes, interfaces, and generic parameters along with the ability to ultimately resolve to a Class.\nResolvableTypes may be obtained from fields, method parameters, method returns or classes. Most methods on this class will themselves return ResolvableTypes, allowing easy navigation.\n\r\r 该类封装了 Java 原生的 Type 类型，提供了获取父类、接口、泛型参数的服务，同时能够最终将结果解析为 Class 类型。\nResolvableTypes 可以使用 字段、方法参数、方法返回值、Class 等方式获取。该类中的大部分方法都将返回ResolvableTypes，方便后续的调用。\n官方提供的示例代码如下所示：\n1 2 3 4 5 6 7 8 9 10 11  private HashMapInteger, ListString myMap; public void example() { ResolvableType t = ResolvableType.forField(getClass().getDeclaredField(\"myMap\")); t.getSuperType(); // AbstractMap  t.asMap(); // Map  t.getGeneric(0).resolve(); // Integer  t.getGeneric(1).resolve(); // List  t.getGeneric(1); // List  t.resolveGeneric(1, 0); // String }   ResolvableType 在 Spring 事件机制中的应用 Spring 的事件机制（观察者模式）使用到了 ResolvableType 对广播的事件进行筛选，使得某监听器仅能监听某类型的事件，示例如下：自定义事件 MyEvent 及监听器 MyListener，该监听器指定泛型为 MyEvent：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  public class MyListener implements ApplicationListenerMyEvent { @Override public void onApplicationEvent(MyEvent event) { System.out.printf(\"My Event, current person: %s %n\", event.getSource()); } } class MyEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; public MyEvent(Object source) { super(source); } }   同时，再定义一个监听器，指定其泛型为 ContextRefreshedEvent，在Spring的 finishRefresh 阶段会由容器发布该事件：\n1 2 3 4 5 6  public class ContextRefreshedListener implements ApplicationListenerContextRefreshedEvent { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.printf(\"ContextRefreshed, current source: %s %n\", event.getSource()); } }   main 方法如下：\n1 2 3 4 5 6 7 8 9 10 11 12  public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions(\"META-INF/overview.xml\"); // 此过程中会由context发布ContextRefreshedEvent  context.refresh(); System.out.println(\"--------------------------------\"); // 发布自定义的MyEvent  context.publishEvent(new MyEvent(SpringIocDemo.class)); }   结果如下：\n1 2 3  ContextRefreshed, current source: org.springframework.context.support.GenericApplicationContext@6acbcfc0 -------------------------------- My Event, current person: class cn.liyangjie.spring.SpringIocDemo   可以发现，两个监听器监听的事件不同，不会互相影响，这正是使用了 ResolvableType 对泛型进行了处理，核心代码位于 AbstractApplicationEventMulticaster 如下：\n1 2 3 4 5 6 7  protected boolean supportsEvent( ApplicationListener listener, ResolvableType eventType, @Nullable Class sourceType) { GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) \u0026\u0026 smartListener.supportsSourceType(sourceType)); }   可以看到，将原始的 ApplicationListener 转换为了 GenericApplicationListenerAdapter，该适配器的构造器如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  public GenericApplicationListenerAdapter(ApplicationListener delegate) { Assert.notNull(delegate, \"Delegate listener must not be null\"); this.delegate = (ApplicationListenerApplicationEvent) delegate; this.declaredEventType = resolveDeclaredEventType(this.delegate); } @Nullable private static ResolvableType resolveDeclaredEventType(ApplicationListenerApplicationEvent listener) { ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass()); if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) { Class targetClass = AopUtils.getTargetClass(listener); if (targetClass != listener.getClass()) { declaredEventType = resolveDeclaredEventType(targetClass); } } return declaredEventType; } @Nullable static ResolvableType resolveDeclaredEventType(Class listenerType) { ResolvableType eventType = eventTypeCache.get(listenerType); if (eventType == null) { // 1位置，重点！！  eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric(); eventTypeCache.put(listenerType, eventType); } return (eventType != ResolvableType.NONE ? eventType : null); }   代码中的 1 位置为重点部分， ResolvableType 解析了 ApplicationListener 具体的泛型类型。最后， supportsEventType 方法调用了 ResolvableType 的 isAssignableFrom 方法，以便监听器能接收泛型声明的类型及其子类事件：\n1 2 3 4 5 6 7 8 9 10 11 12 13  public boolean supportsEventType(ResolvableType eventType) { if (this.delegate instanceof GenericApplicationListener) { return ((GenericApplicationListener) this.delegate).supportsEventType(eventType); } else if (this.delegate instanceof SmartApplicationListener) { Class extends ApplicationEvent eventClass = (Class extends ApplicationEvent) eventType.resolve(); return (eventClass != null \u0026\u0026 ((SmartApplicationListener) this.delegate).supportsEventType(eventClass)); } else { // 1位置，重点  return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType)); } }   现新增一个事件 MyEventChild，继承 MyEvent，并在代码中发布该事件，可以发现 MyListener 也能接收到该事件：\n1 2 3 4 5 6  class MyEventChild extends MyEvent { private static final long serialVersionUID = 1L; public MyEventChild(Object source) { super(source); } }   1 2 3 4 5 6 7 8 9 10 11 12 13 14  public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions(\"META-INF/overview.xml\"); // 此过程中会由context发布ContextRefreshedEvent  context.refresh(); System.out.println(\"--------------------------------\"); // 发布自定义的MyEvent  context.publishEvent(new MyEvent(SpringIocDemo.class)); // 将事件源修改为特定字符串以区分  context.publishEvent(new MyEventChild(\"My Event Child\")); }   结果如下：\n1 2 3 4  ContextRefreshed, current source: org.springframework.context.support.GenericApplicationContext@6acbcfc0 -------------------------------- My Event, current person: class cn.liyangjie.spring.SpringIocDemo My Event, current person: My Event Child   ",
  "wordCount" : "1749",
  "inLanguage": "cn",
  "image":"https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg","datePublished": "2021-09-13T21:56:33+08:00",
  "dateModified": "2021-09-13T21:56:33+08:00",
  "author":[{
    "@type": "Person",
    "name": "SadBird"
  }],
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.liyangjie.cn/posts/work/spring-generic/"
  },
  "publisher": {
    "@type": "Organization",
    "name": "染竹君的个人博客",
    "logo": {
      "@type": "ImageObject",
      "url": "https://www.liyangjie.cn/favicon/favicon.ico"
    }
  }
}
</script>
</head>

<body class="">
    <a id="top"></a>
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    } else if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>

<header class="header">
    <nav class="nav">
        <div class="logo">
            <a href="https://www.liyangjie.cn/" accesskey="h" title="染竹君的小站 (Alt + H)">
                        
                    <img src="https://www.liyangjie.cn/img/logo_hue7837a3d2f79ccdd94da0537755daebc_15738_0x30_resize_box_3.png" alt="logo" aria-label="logo"
                        height="30">染竹君的小站</a>
            <span class="logo-switches">
                <button id="theme-toggle" accesskey="t" title="(Alt + T)">
                    <svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                    </svg>
                    <svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
            </span>
        </div>
        <ul id="menu">
            <li>
                <a href="https://www.liyangjie.cn/categories/" title="分类">
                    <span>分类</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/tags/" title="标签">
                    <span>标签</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/archives/" title="归档">
                    <span>归档</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/search/" title="搜索">
                    <span>搜索</span>
                </a>
            </li>
        </ul>
    </nav>
</header>
<main class="main">

<article class="post-single">
  <header class="post-header">
    <div class="breadcrumbs"><a href="https://www.liyangjie.cn/">Home</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/">Posts</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/work/">Work</a></div>
    <h1 class="post-title">
      Spring 泛型
    </h1>
    <div class="post-meta"><span title='2021-09-13 21:56:33 +0800 CST'>September 13, 2021</span>&nbsp;·&nbsp;9 min&nbsp;·&nbsp;SadBird&nbsp;|&nbsp;<a href="https://github.com/YazidLee/hugo-backup/tree/master/content/posts/work/Spring%e6%b3%9b%e5%9e%8b.md" rel="noopener noreferrer" target="_blank">Suggest Changes</a>
</div>
  </header> <figure class="entry-cover"><img class="lazy" src="/svg/loading.min.svg"  data-src="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" loading="lazy" alt="Spring泛型">
        
</figure><div class="toc">
    <details >
        <summary accesskey="c" title="(Alt + C)">
            <span class="details">Table of Contents</span>
        </summary>

        <div class="inner"><ul>
                <li>
                    <a href="#type-%e6%a6%82%e8%bf%b0" aria-label="Type 概述">Type 概述</a></li>
                <li>
                    <a href="#parameterizedtype" aria-label="ParameterizedType">ParameterizedType</a></li>
                <li>
                    <a href="#typevariable" aria-label="TypeVariable">TypeVariable</a></li>
                <li>
                    <a href="#genericarraytype" aria-label="GenericArrayType">GenericArrayType</a></li>
                <li>
                    <a href="#wildcardtype" aria-label="WildcardType">WildcardType</a></li>
                <li>
                    <a href="#spring-%e4%b8%ad%e7%9a%84-resolvabletype" aria-label="Spring 中的 ResolvableType">Spring 中的 ResolvableType</a><ul>
                        
                <li>
                    <a href="#resolvabletype-%e5%9c%a8-spring-%e4%ba%8b%e4%bb%b6%e6%9c%ba%e5%88%b6%e4%b8%ad%e7%9a%84%e5%ba%94%e7%94%a8" aria-label="ResolvableType 在 Spring 事件机制中的应用">ResolvableType 在 Spring 事件机制中的应用</a>
                </li>
            </ul>
            </li>
            </ul>
        </div>
    </details>
</div>

  <div class="post-content"><h2 id="type-概述">Type 概述<a hidden class="anchor" aria-hidden="true" href="#type-概述">#</a></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Type</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span> <span class="n">String</span> <span class="nf">getTypeName</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">toString</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="details admonition quote open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-quote-right fa-fw"></i>Java Type<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Type is the common super interface for all types in the Java programming language. These include <strong>raw types</strong>, <strong>parameterized types</strong>, <strong>array types</strong>, <strong>type variables</strong> and <strong>primitive types</strong>.</div>
        </div>
    </div>
<p>JDK 中的 <code>Type</code> 是一种高级抽象，其类图关系如下，它代表了 Java 中所有与 <strong>类型</strong> 相关的概念，此处的 <strong>类型</strong> 与我们常用的 <strong>基础数据类型</strong> 和 <strong>引用数据类型</strong> 的概念不同，它的抽象层级更高，概念更加宽泛，主要用于支持泛型中的类型处理。</p>
<p><figure><a class="lightgallery" href="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png" title="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png" data-thumbnail="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png"
            loading="lazy"
            title="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png"
            alt="https://i.loli.net/2021/09/25/6qcd8TzKnCu1NB3.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>除了我们熟悉的 <code>Class</code> 之外，还有如下子接口：</p>
<ul>
<li><code>ParameterizedType</code></li>
<li><code>TypeVariable</code></li>
<li><code>GenericArrayType</code></li>
<li><code>WildcardType</code></li>
</ul>
<p>下面小节将分别使用示例代码进行介绍。</p>
<hr>
<h2 id="parameterizedtype">ParameterizedType<a hidden class="anchor" aria-hidden="true" href="#parameterizedtype">#</a></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">ParameterizedType</span> <span class="kd">extends</span> <span class="n">Type</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Type</span><span class="o">[]</span> <span class="nf">getActualTypeArguments</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Type</span> <span class="nf">getRawType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Type</span> <span class="nf">getOwnerType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>ParameterizedType</code> 形如 <code>List&lt;String&gt;</code> ，即原生类型加上泛型参数的形式，其中，泛型参数可以有多个，并且既可以是实际具体类型（如 <code>String</code>），也可以是参数形式（如 <code>T,K,E</code>），还可以是通配符形式（如 <code>? extends Number</code>）。各方法返回结果示例如下：</p>
<p><figure><a class="lightgallery" href="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png" title="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png" data-thumbnail="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png"
            loading="lazy"
            title="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png"
            alt="https://i.loli.net/2021/09/25/l13gCTuvsIMOxRw.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>以下代码使用反射的方式进行实验，其中 <code>getGenericType()</code> 方法可以获取字段的泛型，对 <code>getActualTypeArguments()</code> 方法获取到的类型数组进行进一步的分析，可以发现它支持所有的 <code>Type</code> 子类型，代码有些部分暂时看不懂可以先放放，等后续章节看完再回头来看：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ParameterizedTypeDemo</span><span class="o">&lt;</span><span class="n">K</span> <span class="kd">extends</span> <span class="n">Number</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Map</span><span class="o">.</span><span class="na">Entry</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">entry</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">stringList</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Map</span><span class="o">&lt;</span><span class="n">K</span><span class="o">[],</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">Number</span><span class="o">&gt;</span> <span class="n">mapWithWildcard</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;&gt;</span> <span class="n">mapWithList</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">OuterClass</span><span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;.</span><span class="na">InnerClass</span><span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;</span> <span class="n">ownerTest</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">clazz</span> <span class="o">=</span> <span class="n">ParameterizedTypeDemo</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Field</span><span class="o">[]</span> <span class="n">declaredFields</span> <span class="o">=</span> <span class="n">clazz</span><span class="o">.</span><span class="na">getDeclaredFields</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">printHorizonLine</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">declaredFields</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">ParameterizedTypeDemo</span><span class="o">::</span><span class="n">showFieldType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printHorizonLine</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">showFieldType</span><span class="o">(</span><span class="n">Field</span> <span class="n">field</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span> <span class="n">type</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="na">getGenericType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">type</span> <span class="k">instanceof</span> <span class="n">ParameterizedType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">printFieldTypeInfo</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="n">type</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">printHorizonLine</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printFieldTypeInfo</span><span class="o">(</span><span class="n">Field</span> <span class="n">field</span><span class="o">,</span> <span class="n">Type</span> <span class="n">type</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ParameterizedType</span> <span class="n">parameterizedType</span> <span class="o">=</span> <span class="o">(</span><span class="n">ParameterizedType</span><span class="o">)</span> <span class="n">type</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span> <span class="n">rawType</span> <span class="o">=</span> <span class="n">parameterizedType</span><span class="o">.</span><span class="na">getRawType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span><span class="o">[]</span> <span class="n">actualTypeArguments</span> <span class="o">=</span> <span class="n">parameterizedType</span><span class="o">.</span><span class="na">getActualTypeArguments</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span> <span class="n">ownerType</span> <span class="o">=</span> <span class="n">parameterizedType</span><span class="o">.</span><span class="na">getOwnerType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;Field &#39;%s&#39; ParameterizedType %s attrs: \n&#34;</span><span class="o">,</span> <span class="n">field</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">type</span><span class="o">.</span><span class="na">getTypeName</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[RawType]: %s, [OwnerType]: %s \n&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">rawType</span><span class="o">,</span> <span class="n">ownerType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;[ActualTypeArguments]:&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">actualTypeArguments</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">ParameterizedTypeDemo</span><span class="o">::</span><span class="n">printActualArguments</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printActualArguments</span><span class="o">(</span><span class="n">Type</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">t</span> <span class="k">instanceof</span> <span class="n">ParameterizedType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;(P) %s \n&#34;</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">t</span> <span class="k">instanceof</span> <span class="n">TypeVariable</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;(T) %s \n&#34;</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">t</span> <span class="k">instanceof</span> <span class="n">WildcardType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;(W) %s \n&#34;</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">t</span> <span class="k">instanceof</span> <span class="n">GenericArrayType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;(A) %s \n&#34;</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">t</span> <span class="k">instanceof</span> <span class="n">Class</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;(C) %s \n&#34;</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">OuterClass</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">class</span> <span class="nc">InnerClass</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出结果为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">Field</span> <span class="err">&#39;</span><span class="n">entry</span><span class="err">&#39;</span> <span class="n">ParameterizedType</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map$Entry</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">&gt;</span> <span class="n">attrs</span><span class="o">:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">RawType</span><span class="o">]:</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map$Entry</span><span class="o">,</span> <span class="o">[</span><span class="n">OwnerType</span><span class="o">]:</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">ActualTypeArguments</span><span class="o">]:</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">T</span><span class="o">)</span> <span class="n">K</span> 
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">C</span><span class="o">)</span> <span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span> 
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">Field</span> <span class="err">&#39;</span><span class="n">stringList</span><span class="err">&#39;</span> <span class="n">ParameterizedType</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">&lt;</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">&gt;</span> <span class="n">attrs</span><span class="o">:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">RawType</span><span class="o">]:</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">,</span> <span class="o">[</span><span class="n">OwnerType</span><span class="o">]:</span> <span class="kc">null</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">ActualTypeArguments</span><span class="o">]:</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">C</span><span class="o">)</span> <span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span> 
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">Field</span> <span class="err">&#39;</span><span class="n">mapWithWildcard</span><span class="err">&#39;</span> <span class="n">ParameterizedType</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map</span><span class="o">&lt;</span><span class="n">K</span><span class="o">[],</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Number</span><span class="o">&gt;</span> <span class="n">attrs</span><span class="o">:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">RawType</span><span class="o">]:</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map</span><span class="o">,</span> <span class="o">[</span><span class="n">OwnerType</span><span class="o">]:</span> <span class="kc">null</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">ActualTypeArguments</span><span class="o">]:</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">A</span><span class="o">)</span> <span class="n">K</span><span class="o">[]</span> 
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">W</span><span class="o">)</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Number</span> 
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">Field</span> <span class="err">&#39;</span><span class="n">mapWithList</span><span class="err">&#39;</span> <span class="n">ParameterizedType</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map</span><span class="o">&lt;</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">,</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">&lt;</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">&gt;&gt;</span> <span class="n">attrs</span><span class="o">:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">RawType</span><span class="o">]:</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Map</span><span class="o">,</span> <span class="o">[</span><span class="n">OwnerType</span><span class="o">]:</span> <span class="kc">null</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">ActualTypeArguments</span><span class="o">]:</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">C</span><span class="o">)</span> <span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span> 
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">P</span><span class="o">)</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">&lt;</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">&gt;</span> 
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">Field</span> <span class="err">&#39;</span><span class="n">ownerTest</span><span class="err">&#39;</span> <span class="n">ParameterizedType</span> <span class="n">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">OuterClass</span><span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;</span><span class="n">$InnerClass</span><span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;</span> <span class="n">attrs</span><span class="o">:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">RawType</span><span class="o">]:</span> <span class="kd">class</span> <span class="nc">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">OuterClass$InnerClass</span><span class="o">,</span> <span class="o">[</span><span class="n">OwnerType</span><span class="o">]:</span> <span class="n">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">OuterClass</span><span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">ActualTypeArguments</span><span class="o">]:</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="n">T</span><span class="o">)</span> <span class="n">K</span> 
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="typevariable">TypeVariable<a hidden class="anchor" aria-hidden="true" href="#typevariable">#</a></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">TypeVariable</span><span class="o">&lt;</span><span class="n">D</span> <span class="kd">extends</span> <span class="n">GenericDeclaration</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="n">Type</span><span class="o">,</span> <span class="n">AnnotatedElement</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Type</span><span class="o">[]</span> <span class="nf">getBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">D</span> <span class="nf">getGenericDeclaration</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">String</span> <span class="nf">getName</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">AnnotatedType</span><span class="o">[]</span> <span class="nf">getAnnotatedBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>TypeVariable</code> 表示的是 <strong>类型参数</strong> 的概念，即泛型定义中使用到的 <code>T, K, E</code> 等，如 JDK 中提供的 <code>List</code> 类，其含有一个 <code>TypeVariable</code> 为 <code>E</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">List</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;...</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>再次观察 <code>TypeVariable</code> 接口可以发现其自身含有一个上界为 <code>GenericDeclaration</code> 的类型参数，其类图如下：</p>
<p><figure><a class="lightgallery" href="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png" title="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png" data-thumbnail="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png"
            loading="lazy"
            title="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png"
            alt="https://i.loli.net/2021/09/25/vtgrSHYycdLikDb.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>字面意义上，它表示的是 <strong>泛型声明</strong>，Java 规范中，泛型的定义可以出现在 <strong>类</strong> 和 <strong>方法</strong> 上（构造器也可归属于方法），正好对应了该上图中的 3 个实现类。因此 <code>getGenericDeclaration()</code> 方法是为了获取该参数所定义的「位置」。以下代码分别定义了 4 个 <code>TypeVariable</code> ，分别展示了 3 种不同位置及 Java8 后引入的 <code>TypeAnnotation</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Target</span><span class="o">({</span><span class="n">ElementType</span><span class="o">.</span><span class="na">TYPE_USE</span><span class="o">})</span>
</span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="o">(</span><span class="n">RetentionPolicy</span><span class="o">.</span><span class="na">RUNTIME</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="nd">@interface</span> <span class="n">TypeBoundAnnotationT</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Target</span><span class="o">({</span><span class="n">ElementType</span><span class="o">.</span><span class="na">TYPE_USE</span><span class="o">})</span>
</span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="o">(</span><span class="n">RetentionPolicy</span><span class="o">.</span><span class="na">RUNTIME</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="nd">@interface</span> <span class="n">TypeBoundAnnotationE</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TypeVariableDemo</span><span class="o">&lt;</span><span class="n">T</span> <span class="kd">extends</span> <span class="nd">@TypeBoundAnnotationT</span> <span class="n">Comparable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;,</span> 
</span></span><span class="line"><span class="cl">  <span class="n">E</span> <span class="kd">extends</span> <span class="nd">@TypeBoundAnnotationT</span> <span class="nd">@TypeBoundAnnotationE</span> <span class="n">Number</span> <span class="o">&amp;</span> 
</span></span><span class="line"><span class="cl">  <span class="nd">@TypeBoundAnnotationE</span> <span class="n">CharSequence</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">typeVariableClazz</span> <span class="o">=</span> <span class="n">TypeVariableDemo</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">TypeVariable</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">GenericDeclaration</span><span class="o">&gt;[]</span> <span class="n">typeParameters</span> <span class="o">=</span> <span class="n">typeVariableClazz</span><span class="o">.</span><span class="na">getTypeParameters</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">printHorizonLine</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">typeParameters</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">TypeVariableDemo</span><span class="o">::</span><span class="n">showTypeVariable</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">methodAndConstructorTestClazz</span> <span class="o">=</span> <span class="n">MethodAndConstructorTest</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Constructor</span><span class="o">&lt;?&gt;[]</span> <span class="n">declaredConstructors</span> <span class="o">=</span> <span class="n">methodAndConstructorTestClazz</span><span class="o">.</span><span class="na">getDeclaredConstructors</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Method</span><span class="o">[]</span> <span class="n">declaredMethods</span> <span class="o">=</span> <span class="n">methodAndConstructorTestClazz</span><span class="o">.</span><span class="na">getDeclaredMethods</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">showMethodTypeVariables</span><span class="o">(</span><span class="n">declaredConstructors</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">showMethodTypeVariables</span><span class="o">(</span><span class="n">declaredMethods</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printHorizonLine</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">showTypeVariable</span><span class="o">(</span><span class="n">TypeVariable</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">GenericDeclaration</span><span class="o">&gt;</span> <span class="n">tv</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">name</span> <span class="o">=</span> <span class="n">tv</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span><span class="o">[]</span> <span class="n">bounds</span> <span class="o">=</span> <span class="n">tv</span><span class="o">.</span><span class="na">getBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">AnnotatedType</span><span class="o">[]</span> <span class="n">annotatedBounds</span> <span class="o">=</span> <span class="n">tv</span><span class="o">.</span><span class="na">getAnnotatedBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">GenericDeclaration</span> <span class="n">genericDeclaration</span> <span class="o">=</span> <span class="n">tv</span><span class="o">.</span><span class="na">getGenericDeclaration</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[TypeVariableName]: %s\n&#34;</span><span class="o">,</span> <span class="n">name</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[Bounds]: %s\n&#34;</span><span class="o">,</span> <span class="n">Arrays</span><span class="o">.</span><span class="na">toString</span><span class="o">(</span><span class="n">bounds</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[GenericDeclaration]: %s\n&#34;</span><span class="o">,</span> <span class="n">genericDeclaration</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;[AnnotatedBounds]: &#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">annotatedBounds</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;%s\n&#34;</span><span class="o">,</span> <span class="n">Arrays</span><span class="o">.</span><span class="na">toString</span><span class="o">(</span><span class="n">a</span><span class="o">.</span><span class="na">getAnnotations</span><span class="o">()));</span>
</span></span><span class="line"><span class="cl">        <span class="o">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">printHorizonLine</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">showMethodTypeVariables</span><span class="o">(</span><span class="n">GenericDeclaration</span><span class="o">[]</span> <span class="n">declaredConstructors</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">declaredConstructors</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">c</span> <span class="o">-&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">TypeVariable</span><span class="o">&lt;?&gt;[]</span> <span class="n">constructorTypeParameters</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">getTypeParameters</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">constructorTypeParameters</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">TypeVariableDemo</span><span class="o">::</span><span class="n">showTypeVariable</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">});</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MethodAndConstructorTest</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="n">K</span><span class="o">&gt;</span> <span class="nf">MethodAndConstructorTest</span><span class="o">(</span><span class="n">K</span> <span class="n">k</span><span class="o">){}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="nf">methodTest</span><span class="o">(</span><span class="n">S</span> <span class="n">s</span><span class="o">){}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出结果如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">TypeVariableName</span><span class="o">]:</span> <span class="n">T</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Bounds</span><span class="o">]:</span> <span class="o">[</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Comparable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">GenericDeclaration</span><span class="o">]:</span> <span class="kd">class</span> <span class="nc">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">TypeVariableDemo</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">AnnotatedBounds</span><span class="o">]:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="nd">@cn.liyangjie.spring.TypeBoundAnnotationT</span><span class="o">()]</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">TypeVariableName</span><span class="o">]:</span> <span class="n">E</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Bounds</span><span class="o">]:</span> <span class="o">[</span><span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Number</span><span class="o">,</span> <span class="kd">interface</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">CharSequence</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">GenericDeclaration</span><span class="o">]:</span> <span class="kd">class</span> <span class="nc">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">TypeVariableDemo</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">AnnotatedBounds</span><span class="o">]:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="nd">@cn.liyangjie.spring.TypeBoundAnnotationT</span><span class="o">(),</span> <span class="nd">@cn.liyangjie.spring.TypeBoundAnnotationE</span><span class="o">()]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="nd">@cn.liyangjie.spring.TypeBoundAnnotationE</span><span class="o">()]</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">TypeVariableName</span><span class="o">]:</span> <span class="n">K</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Bounds</span><span class="o">]:</span> <span class="o">[</span><span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Object</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">GenericDeclaration</span><span class="o">]:</span> <span class="n">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">MethodAndConstructorTest</span><span class="o">(</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Object</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">AnnotatedBounds</span><span class="o">]:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">TypeVariableName</span><span class="o">]:</span> <span class="n">S</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Bounds</span><span class="o">]:</span> <span class="o">[</span><span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Object</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">GenericDeclaration</span><span class="o">]:</span> <span class="kd">public</span> <span class="kt">void</span> <span class="n">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">MethodAndConstructorTest</span><span class="o">.</span><span class="na">methodTest</span><span class="o">(</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Object</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">AnnotatedBounds</span><span class="o">]:</span> 
</span></span><span class="line"><span class="cl"><span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="genericarraytype">GenericArrayType<a hidden class="anchor" aria-hidden="true" href="#genericarraytype">#</a></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">GenericArrayType</span> <span class="kd">extends</span> <span class="n">Type</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Type</span> <span class="nf">getGenericComponentType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>GenericArrayType</code> 表示形如 <code>T[], T[][]</code> 等 <strong>泛型数组</strong>，它有一个方法，获取数组中所存储元素的 <code>Type</code>。示例代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">GenericArrayTypeDemo</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">T</span><span class="o">[]</span> <span class="n">tA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">T</span><span class="o">[][]</span> <span class="n">tAA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;[]</span> <span class="n">listA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">List</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">T</span><span class="o">&gt;[]</span> <span class="n">wildcardListA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">String</span><span class="o">[]</span> <span class="n">stringA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">clazz</span> <span class="o">=</span> <span class="n">GenericArrayTypeDemo</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Field</span><span class="o">[]</span> <span class="n">fields</span> <span class="o">=</span> <span class="n">clazz</span><span class="o">.</span><span class="na">getDeclaredFields</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">fields</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">GenericArrayTypeDemo</span><span class="o">::</span><span class="n">showGenericTypeComponentType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">showGenericTypeComponentType</span><span class="o">(</span><span class="n">Field</span> <span class="n">f</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">fieldName</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span> <span class="n">fieldType</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="na">getGenericType</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">fieldType</span> <span class="k">instanceof</span> <span class="n">GenericArrayType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">GenericArrayType</span> <span class="n">genericArrayType</span> <span class="o">=</span> <span class="o">(</span><span class="n">GenericArrayType</span><span class="o">)</span> <span class="n">fieldType</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[Field: %s] Component Type is: %s\n&#34;</span><span class="o">,</span> <span class="n">fieldName</span><span class="o">,</span> <span class="n">genericArrayType</span><span class="o">.</span><span class="na">getGenericComponentType</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[Field: %s] is not GenericArrayType\n&#34;</span><span class="o">,</span> <span class="n">fieldName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出结果如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span><span class="o">:</span> <span class="n">tA</span><span class="o">]</span> <span class="n">Component</span> <span class="n">Type</span> <span class="n">is</span><span class="o">:</span> <span class="n">T</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span><span class="o">:</span> <span class="n">tAA</span><span class="o">]</span> <span class="n">Component</span> <span class="n">Type</span> <span class="n">is</span><span class="o">:</span> <span class="n">T</span><span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span><span class="o">:</span> <span class="n">listA</span><span class="o">]</span> <span class="n">Component</span> <span class="n">Type</span> <span class="n">is</span><span class="o">:</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span><span class="o">:</span> <span class="n">wildcardListA</span><span class="o">]</span> <span class="n">Component</span> <span class="n">Type</span> <span class="n">is</span><span class="o">:</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">List</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span><span class="o">:</span> <span class="n">stringA</span><span class="o">]</span> <span class="n">is</span> <span class="n">not</span> <span class="n">GenericArrayType</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>GenericType</code> 相对来说还是好理解的，数组中的元素类型即为数组拿掉一个 <code>[]</code> 符号后对应的类型。</p>
<p>为了提升理解，这里留下一个问题：上述输出结果中， <code>T, T[], List&lt;T&gt;, List&lt;? extends T&gt;</code>  分别是什么类型？</p>
<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>答案<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content"><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">[</span>Field: tA<span class="s1">&#39;s component type] is TypeVariable
</span></span></span><span class="line"><span class="cl"><span class="s1">[Field: tAA&#39;</span>s component type<span class="o">]</span> is GenericArrayType
</span></span><span class="line"><span class="cl"><span class="o">[</span>Field: listA<span class="s1">&#39;s component type] is ParameterizedType
</span></span></span><span class="line"><span class="cl"><span class="s1">[Field: wildcardListA&#39;</span>s component type<span class="o">]</span> is ParameterizedType
</span></span></code></pre></td></tr></table>
</div>
</div></div>
        </div>
    </div>
<hr>
<h2 id="wildcardtype">WildcardType<a hidden class="anchor" aria-hidden="true" href="#wildcardtype">#</a></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">WildcardType</span> <span class="kd">extends</span> <span class="n">Type</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Type</span><span class="o">[]</span> <span class="nf">getUpperBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Type</span><span class="o">[]</span> <span class="nf">getLowerBounds</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>顾名思义，它表示的是 <code>? extends Number</code> 形式的 <strong>通配符类型</strong>，接口中的两个方法也比较好理解，分别代表通配符的上界和下界。示例代码如下，为了简单起见，仅仅定义了一个 Field，且使用的时候直接取数组下标 0：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">WildcardTypeDemo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">List</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">Number</span><span class="o">&gt;</span> <span class="n">wildcardList</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">clazz</span> <span class="o">=</span> <span class="n">WildcardTypeDemo</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Field</span> <span class="n">wildcardField</span> <span class="o">=</span> <span class="n">clazz</span><span class="o">.</span><span class="na">getDeclaredFields</span><span class="o">()[</span><span class="n">0</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">        <span class="n">Type</span> <span class="n">genericType</span> <span class="o">=</span> <span class="n">wildcardField</span><span class="o">.</span><span class="na">getGenericType</span><span class="o">();</span> <span class="c1">//List&lt;? extends Number&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">genericType</span> <span class="k">instanceof</span> <span class="n">ParameterizedType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">ParameterizedType</span> <span class="n">parameterizedType</span> <span class="o">=</span> <span class="o">(</span><span class="n">ParameterizedType</span><span class="o">)</span> <span class="n">genericType</span><span class="o">;</span> 
</span></span><span class="line"><span class="cl">            <span class="n">Type</span> <span class="n">actualTypeArgument</span> <span class="o">=</span> <span class="n">parameterizedType</span><span class="o">.</span><span class="na">getActualTypeArguments</span><span class="o">()[</span><span class="n">0</span><span class="o">];</span> <span class="c1">// ? extends Number
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="o">(</span><span class="n">actualTypeArgument</span> <span class="k">instanceof</span> <span class="n">WildcardType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">WildcardType</span> <span class="n">wildcardType</span> <span class="o">=</span> <span class="o">(</span><span class="n">WildcardType</span><span class="o">)</span> <span class="n">actualTypeArgument</span><span class="o">;</span> 
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[Field %s is WildcardType]: %s, its upper bound is %s and lower bound is %s&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">wildcardField</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">wildcardType</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">Arrays</span><span class="o">.</span><span class="na">toString</span><span class="o">(</span><span class="n">wildcardType</span><span class="o">.</span><span class="na">getUpperBounds</span><span class="o">()),</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">Arrays</span><span class="o">.</span><span class="na">toString</span><span class="o">(</span><span class="n">wildcardType</span><span class="o">.</span><span class="na">getLowerBounds</span><span class="o">()));</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出结果为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="o">[</span><span class="n">Field</span> <span class="n">wildcardList</span> <span class="n">is</span> <span class="n">WildcardType</span><span class="o">]:</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Number</span><span class="o">,</span> <span class="n">its</span> <span class="n">upper</span> <span class="n">bound</span> <span class="n">is</span> <span class="o">[</span><span class="kd">class</span> <span class="nc">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">Number</span><span class="o">]</span> <span class="n">and</span> <span class="n">lower</span> <span class="n">bound</span> <span class="n">is</span> <span class="o">[]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="spring-中的-resolvabletype">Spring 中的 ResolvableType<a hidden class="anchor" aria-hidden="true" href="#spring-中的-resolvabletype">#</a></h2>
<p>惯例先贴出 Spring 官方对于该类的简介：</p>
<div class="details admonition quote open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-quote-right fa-fw"></i>ResolvableType<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content"><p>Encapsulates a Java Type, providing access to supertypes, interfaces, and generic parameters along with the ability to ultimately resolve to a Class.</p>
<p><strong>ResolvableType</strong>s may be obtained from fields, method parameters, method returns or classes. Most methods on this class will themselves return <strong>ResolvableType</strong>s, allowing easy navigation.</p>
</div>
        </div>
    </div>
<p>该类封装了 Java 原生的 <code>Type</code> 类型，提供了获取父类、接口、泛型参数的服务，同时能够最终将结果解析为 <code>Class</code> 类型。</p>
<p><code>ResolvableTypes</code> 可以使用 <strong>字段</strong>、方法参数、方法返回值、<code>Class</code> 等方式获取。该类中的大部分方法都将返回<code>ResolvableTypes</code>，方便后续的调用。</p>
<p>官方提供的示例代码如下所示：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span> <span class="n">HashMap</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">,</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;&gt;</span> <span class="n">myMap</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">example</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">ResolvableType</span> <span class="n">t</span> <span class="o">=</span> <span class="n">ResolvableType</span><span class="o">.</span><span class="na">forField</span><span class="o">(</span><span class="n">getClass</span><span class="o">().</span><span class="na">getDeclaredField</span><span class="o">(</span><span class="s">&#34;myMap&#34;</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">   <span class="n">t</span><span class="o">.</span><span class="na">getSuperType</span><span class="o">();</span> <span class="c1">// AbstractMap&lt;Integer, List&lt;String&gt;&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="n">t</span><span class="o">.</span><span class="na">asMap</span><span class="o">();</span> <span class="c1">// Map&lt;Integer, List&lt;String&gt;&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="n">t</span><span class="o">.</span><span class="na">getGeneric</span><span class="o">(</span><span class="n">0</span><span class="o">).</span><span class="na">resolve</span><span class="o">();</span> <span class="c1">// Integer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="n">t</span><span class="o">.</span><span class="na">getGeneric</span><span class="o">(</span><span class="n">1</span><span class="o">).</span><span class="na">resolve</span><span class="o">();</span> <span class="c1">// List
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="n">t</span><span class="o">.</span><span class="na">getGeneric</span><span class="o">(</span><span class="n">1</span><span class="o">);</span> <span class="c1">// List&lt;String&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="n">t</span><span class="o">.</span><span class="na">resolveGeneric</span><span class="o">(</span><span class="n">1</span><span class="o">,</span> <span class="n">0</span><span class="o">);</span> <span class="c1">// String
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="resolvabletype-在-spring-事件机制中的应用">ResolvableType 在 Spring 事件机制中的应用<a hidden class="anchor" aria-hidden="true" href="#resolvabletype-在-spring-事件机制中的应用">#</a></h3>
<p>Spring 的事件机制（观察者模式）使用到了 <code>ResolvableType</code> 对广播的事件进行筛选，使得某监听器仅能监听某类型的事件，示例如下：自定义事件 <code>MyEvent</code> 及监听器 <code>MyListener</code>，该监听器指定泛型为 <code>MyEvent</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyListener</span> <span class="kd">implements</span> <span class="n">ApplicationListener</span><span class="o">&lt;</span><span class="n">MyEvent</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onApplicationEvent</span><span class="o">(</span><span class="n">MyEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;My Event, current person: %s %n&#34;</span><span class="o">,</span> <span class="n">event</span><span class="o">.</span><span class="na">getSource</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MyEvent</span> <span class="kd">extends</span> <span class="n">ApplicationEvent</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="n">1L</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="nf">MyEvent</span><span class="o">(</span><span class="n">Object</span> <span class="n">source</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">super</span><span class="o">(</span><span class="n">source</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>同时，再定义一个监听器，指定其泛型为 <code>ContextRefreshedEvent</code>，在Spring的 <code>finishRefresh</code> 阶段会由容器发布该事件：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ContextRefreshedListener</span> <span class="kd">implements</span> <span class="n">ApplicationListener</span><span class="o">&lt;</span><span class="n">ContextRefreshedEvent</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onApplicationEvent</span><span class="o">(</span><span class="n">ContextRefreshedEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;ContextRefreshed, current source: %s %n&#34;</span><span class="o">,</span> <span class="n">event</span><span class="o">.</span><span class="na">getSource</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>main</code> 方法如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GenericApplicationContext</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GenericApplicationContext</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">XmlBeanDefinitionReader</span> <span class="n">reader</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlBeanDefinitionReader</span><span class="o">(</span><span class="n">context</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">reader</span><span class="o">.</span><span class="na">loadBeanDefinitions</span><span class="o">(</span><span class="s">&#34;META-INF/overview.xml&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 此过程中会由context发布ContextRefreshedEvent
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">context</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;--------------------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布自定义的MyEvent
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">context</span><span class="o">.</span><span class="na">publishEvent</span><span class="o">(</span><span class="k">new</span> <span class="n">MyEvent</span><span class="o">(</span><span class="n">SpringIocDemo</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>结果如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">ContextRefreshed</span><span class="o">,</span> <span class="n">current</span> <span class="n">source</span><span class="o">:</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">context</span><span class="o">.</span><span class="na">support</span><span class="o">.</span><span class="na">GenericApplicationContext</span><span class="nd">@6acbcfc0</span>
</span></span><span class="line"><span class="cl"><span class="o">--------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">My</span> <span class="n">Event</span><span class="o">,</span> <span class="n">current</span> <span class="n">person</span><span class="o">:</span> <span class="kd">class</span> <span class="nc">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">SpringIocDemo</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>可以发现，两个监听器监听的事件不同，不会互相影响，这正是使用了 <code>ResolvableType</code> 对泛型进行了处理，核心代码位于 <code>AbstractApplicationEventMulticaster</code> 如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="kt">boolean</span> <span class="nf">supportsEvent</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ApplicationListener</span><span class="o">&lt;?&gt;</span> <span class="n">listener</span><span class="o">,</span> <span class="n">ResolvableType</span> <span class="n">eventType</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">sourceType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">GenericApplicationListener</span> <span class="n">smartListener</span> <span class="o">=</span> <span class="o">(</span><span class="n">listener</span> <span class="k">instanceof</span> <span class="n">GenericApplicationListener</span> <span class="o">?</span>
</span></span><span class="line"><span class="cl">            <span class="o">(</span><span class="n">GenericApplicationListener</span><span class="o">)</span> <span class="n">listener</span> <span class="o">:</span> <span class="k">new</span> <span class="n">GenericApplicationListenerAdapter</span><span class="o">(</span><span class="n">listener</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">(</span><span class="n">smartListener</span><span class="o">.</span><span class="na">supportsEventType</span><span class="o">(</span><span class="n">eventType</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="n">smartListener</span><span class="o">.</span><span class="na">supportsSourceType</span><span class="o">(</span><span class="n">sourceType</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>  
</span></span></code></pre></td></tr></table>
</div>
</div><p>可以看到，将原始的 <code>ApplicationListener</code> 转换为了 <code>GenericApplicationListenerAdapter</code>，该适配器的构造器如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="hl"><span class="lnt">24
</span></span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="nf">GenericApplicationListenerAdapter</span><span class="o">(</span><span class="n">ApplicationListener</span><span class="o">&lt;?&gt;</span> <span class="n">delegate</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="o">.</span><span class="na">notNull</span><span class="o">(</span><span class="n">delegate</span><span class="o">,</span> <span class="s">&#34;Delegate listener must not be null&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">.</span><span class="na">delegate</span> <span class="o">=</span> <span class="o">(</span><span class="n">ApplicationListener</span><span class="o">&lt;</span><span class="n">ApplicationEvent</span><span class="o">&gt;)</span> <span class="n">delegate</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">.</span><span class="na">declaredEventType</span> <span class="o">=</span> <span class="n">resolveDeclaredEventType</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">delegate</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="n">ResolvableType</span> <span class="nf">resolveDeclaredEventType</span><span class="o">(</span><span class="n">ApplicationListener</span><span class="o">&lt;</span><span class="n">ApplicationEvent</span><span class="o">&gt;</span> <span class="n">listener</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ResolvableType</span> <span class="n">declaredEventType</span> <span class="o">=</span> <span class="n">resolveDeclaredEventType</span><span class="o">(</span><span class="n">listener</span><span class="o">.</span><span class="na">getClass</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">declaredEventType</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">declaredEventType</span><span class="o">.</span><span class="na">isAssignableFrom</span><span class="o">(</span><span class="n">ApplicationEvent</span><span class="o">.</span><span class="na">class</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span> <span class="o">=</span> <span class="n">AopUtils</span><span class="o">.</span><span class="na">getTargetClass</span><span class="o">(</span><span class="n">listener</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">targetClass</span> <span class="o">!=</span> <span class="n">listener</span><span class="o">.</span><span class="na">getClass</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">declaredEventType</span> <span class="o">=</span> <span class="n">resolveDeclaredEventType</span><span class="o">(</span><span class="n">targetClass</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">declaredEventType</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl"><span class="kd">static</span> <span class="n">ResolvableType</span> <span class="nf">resolveDeclaredEventType</span><span class="o">(</span><span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">listenerType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ResolvableType</span> <span class="n">eventType</span> <span class="o">=</span> <span class="n">eventTypeCache</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">listenerType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">eventType</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1位置，重点！！
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span>        <span class="n">eventType</span> <span class="o">=</span> <span class="n">ResolvableType</span><span class="o">.</span><span class="na">forClass</span><span class="o">(</span><span class="n">listenerType</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="n">ApplicationListener</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">getGeneric</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">eventTypeCache</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">listenerType</span><span class="o">,</span> <span class="n">eventType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span> 
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">(</span><span class="n">eventType</span> <span class="o">!=</span> <span class="n">ResolvableType</span><span class="o">.</span><span class="na">NONE</span> <span class="o">?</span> <span class="n">eventType</span> <span class="o">:</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>代码中的 1 位置为重点部分， <code>ResolvableType</code> 解析了 <code>ApplicationListener</code> 具体的泛型类型。最后， <code>supportsEventType</code> 方法调用了 <code>ResolvableType</code> 的 <code>isAssignableFrom</code> 方法，以便监听器能接收泛型声明的类型及其子类事件：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="hl"><span class="lnt">11
</span></span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">supportsEventType</span><span class="o">(</span><span class="n">ResolvableType</span> <span class="n">eventType</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">delegate</span> <span class="k">instanceof</span> <span class="n">GenericApplicationListener</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">((</span><span class="n">GenericApplicationListener</span><span class="o">)</span> <span class="k">this</span><span class="o">.</span><span class="na">delegate</span><span class="o">).</span><span class="na">supportsEventType</span><span class="o">(</span><span class="n">eventType</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">delegate</span> <span class="k">instanceof</span> <span class="n">SmartApplicationListener</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">ApplicationEvent</span><span class="o">&gt;</span> <span class="n">eventClass</span> <span class="o">=</span> <span class="o">(</span><span class="n">Class</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="n">ApplicationEvent</span><span class="o">&gt;)</span> <span class="n">eventType</span><span class="o">.</span><span class="na">resolve</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">(</span><span class="n">eventClass</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="o">((</span><span class="n">SmartApplicationListener</span><span class="o">)</span> <span class="k">this</span><span class="o">.</span><span class="na">delegate</span><span class="o">).</span><span class="na">supportsEventType</span><span class="o">(</span><span class="n">eventClass</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1位置，重点
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">declaredEventType</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="k">this</span><span class="o">.</span><span class="na">declaredEventType</span><span class="o">.</span><span class="na">isAssignableFrom</span><span class="o">(</span><span class="n">eventType</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>现新增一个事件 <code>MyEventChild</code>，继承 <code>MyEvent</code>，并在代码中发布该事件，可以发现 <code>MyListener</code> 也能接收到该事件：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MyEventChild</span> <span class="kd">extends</span> <span class="n">MyEvent</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="n">1L</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="nf">MyEventChild</span><span class="o">(</span><span class="n">Object</span> <span class="n">source</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">super</span><span class="o">(</span><span class="n">source</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GenericApplicationContext</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GenericApplicationContext</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">XmlBeanDefinitionReader</span> <span class="n">reader</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlBeanDefinitionReader</span><span class="o">(</span><span class="n">context</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">reader</span><span class="o">.</span><span class="na">loadBeanDefinitions</span><span class="o">(</span><span class="s">&#34;META-INF/overview.xml&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 此过程中会由context发布ContextRefreshedEvent
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">context</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;--------------------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布自定义的MyEvent
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">context</span><span class="o">.</span><span class="na">publishEvent</span><span class="o">(</span><span class="k">new</span> <span class="n">MyEvent</span><span class="o">(</span><span class="n">SpringIocDemo</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 将事件源修改为特定字符串以区分
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">context</span><span class="o">.</span><span class="na">publishEvent</span><span class="o">(</span><span class="k">new</span> <span class="n">MyEventChild</span><span class="o">(</span><span class="s">&#34;My Event Child&#34;</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>结果如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">ContextRefreshed</span><span class="o">,</span> <span class="n">current</span> <span class="n">source</span><span class="o">:</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">context</span><span class="o">.</span><span class="na">support</span><span class="o">.</span><span class="na">GenericApplicationContext</span><span class="nd">@6acbcfc0</span>
</span></span><span class="line"><span class="cl"><span class="o">--------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="n">My</span> <span class="n">Event</span><span class="o">,</span> <span class="n">current</span> <span class="n">person</span><span class="o">:</span> <span class="kd">class</span> <span class="nc">cn</span><span class="o">.</span><span class="na">liyangjie</span><span class="o">.</span><span class="na">spring</span><span class="o">.</span><span class="na">SpringIocDemo</span>
</span></span><span class="line"><span class="cl"><span class="n">My</span> <span class="n">Event</span><span class="o">,</span> <span class="n">current</span> <span class="n">person</span><span class="o">:</span> <span class="n">My</span> <span class="n">Event</span> <span class="n">Child</span>
</span></span></code></pre></td></tr></table>
</div>
</div>

  </div>

  <footer class="post-footer">
    <ul class="post-tags">
      <li><a href="https://www.liyangjie.cn/tags/java/">Java</a></li>
      <li><a href="https://www.liyangjie.cn/tags/spring/">Spring</a></li>
      <li><a href="https://www.liyangjie.cn/tags/generic/">Generic</a></li>
    </ul><div id="post-licensing" class="admonition warning">
    <div class="details-summary admonition-title">
        版权声明 <a rel="license" target="_blank" href="https://creativecommons.org/licenses/by/4.0/deed.zh"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i></a>
    </div>
    
    <div class="details-content">
        <div class="admonition-content">
            <ul>
                <li>本文作者：SadBird。</li>
                <li>本文链接：https://www.liyangjie.cn/posts/work/spring-generic/。</li>
                <li>许可说明：本站所有文章除特殊声明外，均使用 <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="license" target="_blank"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i> CC BY 4.0 </a> 许可协议，转载请注明出处。</li>
            </ul>
        </div>
    </div>
</div>
<nav class="paginav">
  <a class="prev" href="https://www.liyangjie.cn/posts/work/threadlocal-reference/">
    <span class="title"><i class="fas fa-angle-double-left"></i> Prev Page</span>
    <br>
    <span>ThreadLocal 分析（上）——Java 中的引用</span>
  </a>
  <a class="next" href="https://www.liyangjie.cn/posts/work/hashmap-source/">
    <span class="title">Next Page <i class="fas fa-angle-double-right"></i></span>
    <br>
    <span>HashMap 基础</span>
  </a>
</nav>

  </footer>
<footer class="tc-container" id="comment">
    <div class="tc-title"><p class="c-title">Discussion</p></div>
    <div id="tcomments"></div>
</footer>
<script crossorigin="anonymous" src="/js/twikoo.min.64322d6748f9b8b12dfb029616065f8eeed16467444adde58bab7d98c5733adf.js" integrity="sha256-ZDItZ0j5uLEt&#43;wKWFgZfju7RZGdESt3li6t9mMVzOt8="></script>
<script>
    twikoo.init({
        envId: 'https://twikoo-nu-red.vercel.app/',
        el: '#tcomments',
        region: 'ap-shenzhen', 
        
        lang: 'zh-CN', 
    });
</script>
</article>
    </main>
    
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
        <path d="M12 6H0l6-6z" />
    </svg>
</a><script>
    class Accordion {
        constructor(el) {
            
            this.el = el;
            
            this.summary = el.querySelector('summary');
            
            this.content = el.querySelector('.inner');

            
            this.animation = null;
            
            this.isClosing = false;
            
            this.isExpanding = false;
            
            this.summary.addEventListener('click', (e) => this.onClick(e));
        }

        onClick(e) {
            
            e.preventDefault();
            
            this.el.style.overflow = 'hidden';
            
            if (this.isClosing || !this.el.open) {
            this.open();
            
            } else if (this.isExpanding || this.el.open) {
            this.shrink();
            }
        }

        shrink() {
            
            this.isClosing = true;
            
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            
            this.animation.onfinish = () => this.onAnimationFinish(false);
            
            this.animation.oncancel = () => this.isClosing = false;
        }

        open() {
            
            this.el.style.height = `${this.el.offsetHeight}px`;
            
            this.el.open = true;
            
            window.requestAnimationFrame(() => this.expand());
        }

        expand() {
            
            this.isExpanding = true;
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            this.animation.onfinish = () => this.onAnimationFinish(true);
            
            this.animation.oncancel = () => this.isExpanding = false;
        }

        onAnimationFinish(open) {
            
            this.el.open = open;
            
            this.animation = null;
            
            this.isClosing = false;
            this.isExpanding = false;
            
            this.el.style.height = this.el.style.overflow = '';
        }
    }

    document.querySelectorAll('.toc details').forEach((el) => {
        new Accordion(el);
    });
</script>
<a href="#comment" aria-label="go to bottom comment" title="Go to Bottom Comment (Alt + B)" class="top-link bottom-comment" id="bottom-comment" accesskey="b" style="visibility: visible; opacity: 1;">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.338 29.338" fill="currentcolor">
        <path d="M27.184,1.605H2.156C0.967,1.605,0,2.572,0,3.76v17.572c0,1.188,0.967,2.155,2.156,2.155h13.543
		l5.057,3.777c0.414,0.31,0.842,0.468,1.268,0.468c0.789,0,1.639-0.602,1.637-1.923v-2.322h3.523c1.188,0,2.154-0.967,2.154-2.155
		V3.76C29.338,2.572,28.371,1.605,27.184,1.605z M27.34,21.332c0,0.085-0.068,0.155-0.154,0.155h-5.523v3.955l-5.297-3.956H2.156
		c-0.086,0-0.154-0.07-0.154-0.155V3.759c0-0.085,0.068-0.155,0.154-0.155v0.001h25.029c0.086,0,0.154,0.07,0.154,0.155
		L27.34,21.332L27.34,21.332z M5.505,10.792h4.334v4.333H5.505C5.505,15.125,5.505,10.792,5.505,10.792z M12.505,10.792h4.334v4.333
		h-4.334V10.792z M19.505,10.792h4.334v4.333h-4.334V10.792z"/>
    </svg>
</a>

<script>
    let comment = document.getElementById("comment");
    let bottomToComment = document.getElementById("bottom-comment")
    
    document.addEventListener('scroll',function () {
        const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
        const offsetTop = comment.offsetTop
        const scrollTop = document.documentElement.scrollTop
        const top = offsetTop - scrollTop
        if (top <= viewPortHeight + 100) {
            bottomToComment.style.visibility = "hidden";
            bottomToComment.style.opacity = "0";
        } else {
            bottomToComment.style.visibility = "visible";
            bottomToComment.style.opacity = "1";
        }
    })
</script><footer class="footer">
    <span>&copy; 2022 <a href="https://www.liyangjie.cn/">染竹君的个人博客</a></span>
    <span>
        Powered by
        <a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
        <a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
    </span>
    
</footer>

<script crossorigin="anonymous" src="/js/intersection-observer.min.c0ff09623e9f209a66c6130004e0422e1385fd8e8f8baaa14246a49c19827c68.js" integrity="sha256-wP8JYj6fIJpmxhMABOBCLhOF/Y6Pi6qhQkaknBmCfGg="></script>
<script crossorigin="anonymous" src="/js/lazyload.min.min.53e82bf9e8c145b953635b17fa3911ab6f3a8f1e4d37969aeb39d829d30fa6e9.js" integrity="sha256-U&#43;gr&#43;ejBRblTY1sX&#43;jkRq286jx5NN5aa6znYKdMPpuk="></script>


<script crossorigin="anonymous" src="/js/lightgallery.min.5d1410b8c831852c6c62b2dfad75f4e0f046a13e1826f97cd7a742523fab608c.js" integrity="sha256-XRQQuMgxhSxsYrLfrXX04PBGoT4YJvl816dCUj&#43;rYIw="></script>
<script crossorigin="anonymous" src="/js/lg-thumbnail.min.afa3995936244c14f68b85b13d657c368dc787cbcea863179950ac494dab6117.js" integrity="sha256-r6OZWTYkTBT2i4WxPWV8No3Hh8vOqGMXmVCsSU2rYRc="></script>
<script crossorigin="anonymous" src="/js/lg-zoom.min.a47b38d6f7138dce5f712f77780f339b5ec26af3bcd44950bcf736754ec88ed4.js" integrity="sha256-pHs41vcTjc5fcS93eA8zm17CavO81ElQvPc2dU7IjtQ="></script>
<script>
    const lazyLoadInstance = new LazyLoad({
        
    }); 
    
    const config = {
        selector: ".lightgallery",
        mode: "lg-slide",
        plugins: [lgZoom, lgThumbnail],
        speed: 400,
        hideBarsDelay: 2000,
        mousewheel: true,
        thumbnail: true,
        exThumbImage: "data-thumbnail",
        thumbWidth: 80,
        thumbContHeight: 80,
        mobileSettings: { controls: true, showCloseIcon: true, download: true, },
        licenseKey: "28AC9E09-3D8C45D8-8D6124E0-8FF74FF3", 
    };

    lightGallery(document.getElementsByClassName('post-content')[0], config);
</script>
<script>
    let details = document.getElementsByClassName('details')
    details = details || [];
    for (let i = 0; i < details.length; i++) {
        let element = details[i]
        const summary = element.getElementsByClassName('details-summary')[0];
        if (summary) {
            summary.addEventListener('click', () => {
                element.classList.toggle('open');
            }, false);
        }
    }
</script>

<script>
    let menu = document.getElementById('menu')
    if (menu) {
        menu.scrollLeft = localStorage.getItem("menu-scroll-position");
        menu.onscroll = function () {
            localStorage.setItem("menu-scroll-position", menu.scrollLeft);
        }
    }

    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();
            var id = this.getAttribute("href").substr(1);
            if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
                    behavior: "smooth"
                });
            } else {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
            }
            if (id === "top") {
                history.replaceState(null, null, " ");
            } else {
                history.pushState(null, null, `#${id}`);
            }
        });
    });

</script>
<script>
    var mybutton = document.getElementById("top-link");
    window.onscroll = function () {
        if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
            mybutton.style.visibility = "visible";
            mybutton.style.opacity = "1";
        } else {
            mybutton.style.visibility = "hidden";
            mybutton.style.opacity = "0";
        }
    };

</script>
<script>
    document.getElementById("theme-toggle").addEventListener("click", () => {
        if (document.body.className.includes("dark")) {
            document.body.classList.remove('dark');
            localStorage.setItem("pref-theme", 'light');
        } else {
            document.body.classList.add('dark');
            localStorage.setItem("pref-theme", 'dark');
        }
    })

</script>
<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const copybutton = document.createElement('button');
        copybutton.classList.add('copy-code');
        copybutton.innerHTML = 'copy';

        function copyingDone() {
            copybutton.innerHTML = 'copied!';
            setTimeout(() => {
                copybutton.innerHTML = 'copy';
            }, 2000);
        }

        copybutton.addEventListener('click', (cb) => {
            if ('clipboard' in navigator) {
                navigator.clipboard.writeText(codeblock.textContent);
                copyingDone();
                return;
            }

            const range = document.createRange();
            range.selectNodeContents(codeblock);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            try {
                document.execCommand('copy');
                copyingDone();
            } catch (e) { };
            selection.removeRange(range);
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(copybutton);
        } else if (container.parentNode.firstChild == container) {
            
        } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
            
            codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
        } else {
            
            codeblock.parentNode.appendChild(copybutton);
        }
    });
</script>


<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [['$','$'], ['\\(','\\)']],
        processEscapes: true
      }
    });
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script></body>

</html>